package com.chen.file.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.chen.base.utils.file.IoFileUtil;
import com.chen.base.utils.file.WebFileUtil;
import com.chen.file.params.FileSplitDownloadParam;
import com.chen.file.params.FileSplitUploadParam;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @author: ChenJinLong
 * @DATE: 2024/7/25
 **/
@Slf4j
@Service
public class FileServiceImpl implements FileService {

    /** 文件上传下载临时文件目录 */
    @Value("${file-temp}")
    private String tempFileDir;
    @Resource
    private HttpServletRequest request;
    @Resource
    private HttpServletResponse response;

    @Override
    public String uploadFile(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        long startTime = System.currentTimeMillis();
        // 文件存储路径
        String storePath = fileStorePath(getTempDir(), fileName);
        try (BufferedInputStream bis = new BufferedInputStream(file.getInputStream());
             BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.get(storePath)))){

            WebFileUtil.outputStreamWrite(bos, bis);
            long endTime = System.currentTimeMillis();
            log.info("文件上传成功 -> {}, 耗时 -> {}ms", storePath, endTime - startTime);
        } catch (Exception e) {
            log.error("", e);
        }

        return storePath;
    }

    @Override
    @SneakyThrows
    public String fileSplitUpload(FileSplitUploadParam param) {
        Integer totalChunk = param.getTotalChunk();
        String fileName = param.getFileName();
        String md5 = param.getMd5();
        Integer currChunk = param.getCurrentChunk();
        String tempDir = getTempDir();

        String tmpFilePath = jointFilePath(tempDir, currChunk, fileName);
        param.getFile().transferTo(new File(tmpFilePath));

        if (currChunk == totalChunk - 1) {
            // 合并所有分片
            List<String> filePaths = new ArrayList<>();
            for (int i = 0; i < totalChunk; i++) {
                filePaths.add(jointFilePath(tempDir, i, fileName));
            }
            tmpFilePath = fileStorePath(tempDir,  fileName);
            IoFileUtil.mergeFiles(filePaths, tmpFilePath, true, null);
            Assert.isTrue(IoFileUtil.md5(tmpFilePath).equals(md5),"文件损坏，请重新上传! ");
        }

        return tmpFilePath;
    }

    @Override
    public void downloadFile(String filePath) {
        //  文件存在才下载
        File file = new File(filePath);
        WebFileUtil.downToClient(file);
    }

    @Override
    public void fileSplitDownload(FileSplitDownloadParam param) {
        String filePath = param.getFilePath();
        Integer chunkSize = param.getChunkSize();
        Integer currChunk = param.getCurrentChunk();

        try (BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream())){
            File resultFile = new File(filePath);
            Assert.isTrue(resultFile.exists(),"文件不存在，请检查文件路径! filePath -> " + filePath);

            long offset = (long) chunkSize * (currChunk - 1);
            if(Objects.equals(param.getTotalChunk(), currChunk)){
                offset = resultFile.length() - chunkSize;
            }

            WebFileUtil.setDownHeader(request, response, param.getFileName());
            byte[] bytes = IoFileUtil.getFileChunk(offset, chunkSize, filePath);
            out.write(bytes);
            out.flush();
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 默认返回 temp + 日期 的文件目录
     * 如：D:\\temp\\2023-08-11\\
     */
    private String getTempDir(){
        String fileTemp = tempFileDir + LocalDate.now() + "\\";
        File file = new File(fileTemp);
        if (!file.exists()) {
            // 目录不存在且创建成功，返回：true；目录存在，返回：false
            file.mkdirs();
        }
        return fileTemp;
    }

    private String jointFilePath(String filePath,Integer currentChunk,String fileName){
        return filePath + "-" + currentChunk + "-" + fileName + ".part";
    }

    /**
     * 文件存储路径
     */
    private String fileStorePath(String uploadPath, String fileName){
        // 获取文件后缀，如：.zip
        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
        // 拼接文件名
        fileName = IdUtil.objectId() + fileSuffix;
        // 文件存储路径
        return uploadPath + fileName;
    }
}
